package gateway

import (
	"Volta/Config"
	"Volta/Core/Utils/Tools"
	"Volta/Core/gateway/container"
	"Volta/Core/gateway/wspl"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"net/http"
	"reflect"
	"runtime/debug"
	"time"
)

// Configure the upgrader
var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

type WebsocketManager struct {
	WsConn    *websocket.Conn
	WriteChan chan string
	Closed    bool
}

type WsGateway struct {}

//.开启socket服务
func (gateway *WsGateway) Handle(options Options) {
	ctxType = options.CtxType
	http.HandleFunc("/", gateway.GatewayManager)
	fmt.Println("GatewayWorker Start")
	err := http.ListenAndServe(Config.Server.GatewayAddr, nil)
	if err != nil {
		fmt.Printf("ListenAndServe: %s", err.Error())
	}
}

func (gateway *WsGateway) GatewayManager(w http.ResponseWriter, r *http.Request) {
	// Upgrade initial GET wspl to a websocket
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		fmt.Println(err)
	} else {
		go gateway.Alloter(ws)
	}
}

func (gateway *WsGateway) Alloter(ws *websocket.Conn) {
	defer endStack("[volta-gateway]:socket master process closed")
	wsManager := &WebsocketManager{
		WsConn:    ws,
		WriteChan: make(chan string, 1000),
		Closed:    false,
	}
	signal.OnConnect(ws, "guest")
	go wsManager.readProcess()
	go wsManager.writeProcess()
	for {
		//.主线程阻塞 5秒检测一次是否断连
		if wsManager.Closed == true {
			break
		}
		time.Sleep(5 * time.Second)
	}
	//.主线程退出前关闭socket链接,这里会出现空指针错误，defer已经忽略错误不会导致crash
	ws.Close()
}

//.只接收client发来的部分信息
func (wsManager *WebsocketManager) readProcess() {
	defer wsManager.Close("[volta-gateway]:read disconnect")
	for {
		_, message, err := wsManager.WsConn.ReadMessage()
		if err != nil {
			return
		}
		wsManager.gateWayControl(string(message))
	}
}

//.串行向当前client 推送消息
func (wsManager *WebsocketManager) writeProcess() {
	defer wsManager.Close("[volta-gateway]:write disconnect")
	for {
		select {
		case message := <-wsManager.WriteChan:
			err := wsManager.WsConn.WriteMessage(1, []byte(message))
			if err != nil {
				return
			}
		case <-time.After(15 * time.Second):
			//.15秒没有发消息请求，检测一次是否已经断连
			if wsManager.Closed == true {
				return
			}
		}
	}
}

//.向自己发消息
func (wsManager *WebsocketManager) SendSelf(msg interface{}) (err error) {
	jsonB, err := json.Marshal(msg)
	if err != nil {
		return
	}
	select {
	case wsManager.WriteChan <- string(jsonB):
	case <-time.After(2 * time.Second):
		//.2秒无法写进数据,直接中断
		return errors.New("write timeout 2s")
	}
	return
}

//.发送关闭socket链接信号
func (wsManager *WebsocketManager) Close(appName string) {
	wsManager.Closed = true
	if err := recover(); err != nil {
		//将客户端的这次请求头、主体等信息+程序的堆栈信息
		msg := map[string]interface{}{
			"error": err,                   //真正的错误信息
			"wspl":  appName,               //连接句柄信息
			"stack": string(debug.Stack()), //此刻程序的堆栈信息
		}
		fmt.Println(msg)
	}
}

//.Gateway消息控制器
func (wsManager *WebsocketManager) gateWayControl(message string) {
	signal.OnMessage(wsManager.WsConn, message)
	if message == "gate" {
		wsManager.gate()
	}
	//.更新心跳
	if message == "ping" {
		HeartBeatUpdate(wsManager.WsConn)
	}
	//.解析数据包，生成request数据
	request, err := wsManager.gatewayDecode(message)
	if err != nil {
		return
	}
	//.路由转发
	err2 := wsManager.route(request)
	if err2 != nil {
		wsManager.SendSelf(err2.Error())
		return
	}
}

//.ws通信自定协议解析
func (wsManager *WebsocketManager) gatewayDecode(message string) (req *wspl.WsRequest, err error) {
	var wsReq *wspl.WsRequest = &wspl.WsRequest{}
	err = wsReq.Render(&wspl.Request{message})
	if err != nil {
		return
	}
	req = wsReq
	return

}

//.ws任务分配路由
func (wsManager *WebsocketManager) route(req *wspl.WsRequest) (err error) {
	relay, err := container.App.Make(req.Header.Method)
	if err != nil {
		return errors.New("header method not found")
	}
	//.创建relay反射对象 这里多例，为保证并发安全
	//refV := reflect.New(relay.ConType)
	//method := refV.MethodByName(relay.Method)
	//if !method.IsValid() {
	//	return
	//}
	var context Context
	context.Bbo = &wspl.Bbo{
		Request: req,
		Response: &wspl.WsResponse{
			Ctx: ctxType,
			Header: make(map[string]string),
			Data:   make([]byte, 0),
			Extend: make(map[string]string),
		},
	}
	context.WsManager = wsManager
	paramList := make([]reflect.Value, 1)
	paramList[0] = reflect.ValueOf(&context)
	relay.ConValue.Call(paramList)
	return
}

//. 响应客户端
func (wsManager *WebsocketManager) gate() {
	res := map[string]interface{}{
		"type": "gate",
		"msg":  "online",
		"Date": Tools.GetTime(),
	}
	bodyB, _ := json.Marshal(res)
	//.防止线程阻塞
	select {
	case wsManager.WriteChan <- string(bodyB):
	case <-time.After(2 * time.Second):
		//.2秒无法写进数据,直接中断
		return
	}
}

